﻿using System;
using System.Runtime.InteropServices;

namespace SilverlightViewport.Common.Interop
{
    abstract public class AlmostUnknown
    {
        private readonly IntPtr m_structPointer;

        private delegate int AddRefDelegate(IntPtr pStruct);
        private delegate int ReleaseDelegate(IntPtr pStruct);

        private AddRefDelegate AddRefMethod;
        private ReleaseDelegate ReleaseMethod;

        protected AlmostUnknown(IntPtr pStruct)
        {
            m_structPointer = pStruct;
            SetupAlmostUnknownMethods();
        }

        private void SetupAlmostUnknownMethods()
        {
            /* Read the COM v-table pointer */
            IntPtr pVtable = Marshal.ReadIntPtr(m_structPointer);

            const int ADDREF_VTABLE_INDEX = 0;
            const int RELEASE_VTABLE_INDEX = 1;

            /* Get the function pointer */
            IntPtr pFunc = Marshal.ReadIntPtr(pVtable, ADDREF_VTABLE_INDEX * IntPtr.Size);

            /* Cast the function pointer to a .NET delegate */
            AddRefMethod = (AddRefDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(AddRefDelegate));

            /* Rinse and repeat */
            pFunc = Marshal.ReadIntPtr(pVtable, RELEASE_VTABLE_INDEX * IntPtr.Size);

            ReleaseMethod = (ReleaseDelegate)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(ReleaseDelegate));
        }

        public int AddRef()
        {
            return AddRefMethod(m_structPointer);
        }

        public int Release()
        {
            return ReleaseMethod(m_structPointer);
        }

        public IntPtr StructPointer
        {
            get { return m_structPointer; }
        }
    }
}
